import { AxiosRequestConfig, AxiosResponse } from './types'
import AxiosInterceptorManager, { Interceptor } from './AxiosInterceptorManager';
import qs from 'qs'
import parse from 'parse-headers'

export interface Interceptors{
    request: AxiosInterceptorManager<AxiosRequestConfig>,
    response: AxiosInterceptorManager<AxiosResponse>
}
let defaults: AxiosRequestConfig = {
    method: 'get',
    timeout: 0,
    headers: {
        common: {
            accept: 'application/json'
        }
    },
    transformRequest(data, headers){
        headers['content-type'] = 'application/x-www-form-urlencoded';
        return qs.stringify(data);
    },
    transformResponse(data) {
        if (typeof data == 'string')
            data = JSON.parse(data);
        return data;
    }
}
let getStyleMethods = ['get','head','delete','options'];
let postStyleMethods = ['post','put','patch'];
getStyleMethods.forEach((method: string) =>{
    defaults.headers![method] = {}
})
postStyleMethods.forEach((method: string)=>{
    defaults.headers![method] = {}
})
let allMethods = [...getStyleMethods, ...postStyleMethods]

class Axios {
    public defaults: AxiosRequestConfig = defaults;
    public interceptors: Interceptors = { 
        request: new AxiosInterceptorManager<AxiosRequestConfig>(),
        response: new AxiosInterceptorManager<AxiosResponse>()
    }

    request<T>(config: AxiosRequestConfig): Promise<AxiosRequestConfig | AxiosResponse<T>>{ // T修饰返回体data
        if (config.transformRequest && config.data)
            config.data = config.transformRequest(config.data, config.headers = {});
        config.headers = Object.assign(this.defaults.headers, config.headers)

        const chain: Interceptor[] = [{
            onFulfilled: this.dispatchRequest,
            onRejected: undefined
        }];  // 将interceptor拼到请求两边

        this.interceptors.request.interceptors.forEach((interceptor: Interceptor<AxiosRequestConfig> | null) => {
            interceptor && chain.unshift(interceptor)
        });

        this.interceptors.response.interceptors.forEach((interceptor: Interceptor<AxiosResponse> | null) => {
            interceptor && chain.push(interceptor)
        });


        // 将chain中的请求串联起来调用 
        /**
         *  promise.then(onFulfilled, onRejected).
         *  then(onFulfilled, onRejected).
         *  then(onFulfilled, onRejected)
         */

        let promise  = Promise.resolve(config);
        while (chain.length) {
            const  { onFulfilled, onRejected } = chain.shift()!;
            promise = promise.then(onFulfilled, onRejected)
        }
        return promise;
    //    return this.dispatchRequest(config);
    }
    
    dispatchRequest<T>(config: AxiosRequestConfig): Promise<AxiosResponse<T>>{
        return  new Promise<AxiosResponse<T>>((resolve, reject)=>{
            let { method='get', url, params, data, headers, timeout } = config;
            let request:XMLHttpRequest  = new XMLHttpRequest();
            if(params){
                let paramsString = qs.stringify(params);
                url = url + (url!.indexOf('?') === -1 ? '?' : '&') + paramsString;
            }
            request.open(method, url!, true);
            request.responseType = 'json'
            request.onreadystatechange = ()=> {
                if(request.readyState === 4 && request.status !== 0){
                    if(request.status >= 200 && request.status <=300){
                        let response: AxiosResponse = {
                            data: request.response,
                            status: request.status,
                            statusText: request.statusText,
                            headers: parse(request.getAllResponseHeaders()),
                            config: config,
                            request,
                        }
                        if (config.transformResponse) {
                            response.data = config.transformResponse(response.data);
                        }
                        resolve(response)
                    } else {
                         reject(new Error(`Request failed with status code ${request.status}`));
                    }
                }
            }
            if(headers){
                for (let key in headers) {
                    if (key === 'common' || allMethods.includes(key)) {
                        for (let key2 in headers[key]) {
                            request.setRequestHeader(key2, headers[key][key2]);
                        }
                    } else {
                        request.setRequestHeader(key, headers[key]);
                    }
                }
            }
            let body: string | null = null;
            if(data && typeof data === 'object'){
                body = JSON.stringify(data);
            }

            request.onerror = () => { //网络异常
                reject(new Error('Network Error'));
            }
            if(timeout){
                request.timeout = timeout;
                request.ontimeout=()=>{
                    reject(new Error(`timeout of ${timeout}ms exceeded`))
                }
            }
            if (config.cancelToken) {
                config.cancelToken.then((reason: string) => {
                    request.abort();
                    reject(reason);
                });
            } else {
                request.send(body);
            }

       })
    }
}

export default Axios;